﻿using In66.Net.Core.System.Extensions.Expressions;
using In66.Net.Repository.IRepositories.ISqlSugar;
using In66.Net.Share.Models.Dtos;
using Yitter.IdGenerator;

namespace Usr.Api.Services.Impl
{
    public class UserAppService : AbstractAppService, IUserAppService
    {
        private readonly ISugarBaseRepository<User> _userRepository;
        private readonly ISugarBaseRepository<Role> _roleRepository;
        private readonly ISugarBaseRepository<Menu> _menuRepository;
        private readonly CacheService _cacheService;
        private readonly BloomFilterFactory _bloomFilterFactory;

        public UserAppService(
            ISugarBaseRepository<User> userRepository
            , ISugarBaseRepository<Role> roleRepository
            , ISugarBaseRepository<Menu> menuRepository
            , CacheService cacheService
           , BloomFilterFactory bloomFilterFactory)
        {
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _menuRepository = menuRepository;
            _cacheService = cacheService;
            _bloomFilterFactory = bloomFilterFactory;
        }

        public async Task<AppSrvResult<long>> CreateAsync(UserCreationDto input)
        {
            input.TrimStringFields();
            if (await _userRepository.ExistsAsync(x => x.Account == input.Account))
            {
                return Problem(HttpStatusCode.BadRequest, "账号已经存在");
            }

            var user = Mapper.Map<User>(input);
            user.Id = YitIdHelper.NextId();
            user.Account = user.Account.ToLower();
            user.Salt = Random.Shared.Next(5, false);
            user.Password = InfraHelper.Security.MD5(user.Password + user.Salt);

            var cacheKey = _cacheService.ConcatCacheKey(CachingConsts.UserValidatedInfoKeyPrefix, user.Id);
            var bloomFilterCacheKey = _bloomFilterFactory.Create(CachingConsts.BloomfilterOfCacheKey);
            await bloomFilterCacheKey.AddAsync(cacheKey);

            var bloomFilterAccount = _bloomFilterFactory.Create(CachingConsts.BloomfilterOfAccountsKey);
            await bloomFilterAccount.AddAsync(user.Account);

            await _userRepository.InsertAsync(user);

            return user.Id;
        }

        public async Task<AppSrvResult> UpdateAsync(long id, UserUpdationDto input)
        {
            input.TrimStringFields();

            var user = Mapper.Map<User>(input);

            await _userRepository.Update(s => s.Id == user.Id, f => user);

            return AppSrvResult();
        }

        public async Task<AppSrvResult> SetRoleAsync(long id, UserSetRoleDto input)
        {
            var roleIdStr = input.RoleIds.IsNullOrEmpty() ? string.Empty : string.Join(",", input.RoleIds);

            await _userRepository.Update(s => s.Id == id, f => new User()
            {
                RoleIds = roleIdStr
            });

            return AppSrvResult();
        }

        public async Task<AppSrvResult> DeleteAsync(long id)
        {
            await _userRepository.DeleteByIdAsync(id);
            return AppSrvResult();
        }

        public async Task<AppSrvResult> ChangeStatusAsync(long id, int status)
        {
            await _userRepository.Update(s => s.Id == id, f => new User()
            {
                Status = status
            });

            return AppSrvResult();
        }

        public async Task<AppSrvResult> ChangeStatusAsync(IEnumerable<long> ids, int status)
        {
            await _userRepository.Update(s => ids.Contains(s.Id), f => new User()
            {
                Status = status
            });

            return AppSrvResult();
        }

        public async Task<List<string>> GetPermissionsAsync(long userId, IEnumerable<string> requestPermissions, string userBelongsRoleIds)
        {
            if (userBelongsRoleIds.IsNullOrWhiteSpace())
            {
                return default;
            }

            if (requestPermissions.IsBusinessNullOrEmpty())
            {
                return new List<string> { "allow" };
            }

            var roleIds = userBelongsRoleIds.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(x => long.Parse(x.Trim()));

            var allMenuCodes = await _cacheService.GetAllMenuCodesFromCacheAsync();

            var upperCodes = allMenuCodes?.Where(x => roleIds.Contains(x.RoleId)).Select(x => x.Code.ToUpper());
            if (upperCodes.IsBusinessNullOrEmpty())
            {
                return default;
            }

            var result = upperCodes.Intersect(requestPermissions.Select(x => x.ToUpper()));
            return result.ToList();
        }

        public async Task<PageModelVo<UserDto>> GetPagedAsync(UserSearchPagedDto search)
        {
            search.TrimStringFields();
            var whereExpression = ExpressionCreator
                                                .New<User>()
                                                .AndIf(search.Account.IsNotNullOrWhiteSpace(), x => SqlFunc.Contains(x.Account, $"%{search.Account}%"))
                                                .AndIf(search.Name.IsNotNullOrWhiteSpace(), x => SqlFunc.Contains(x.Name, $"%{search.Name}%"));

            var total = await _userRepository.GetCountAsync(whereExpression);
            if (total == 0)
            {
                return new PageModelVo<UserDto>(search);
            }

            var entities = _userRepository.QueryListByClause(whereExpression)
                                            .OrderByDescending(x => x.Id)
                                            .Skip(search.SkipRows())
                                            .Take(search.PageSize)
                                            .ToList();

            var userDtos = Mapper.Map<List<UserDto>>(entities);
            if (userDtos.IsNotNullOrEmpty())
            {
                var deptIds = userDtos.Where(d => d.DeptId is not null).Select(d => d.DeptId).Distinct();
                var depts = (await _cacheService.GetAllOrganizationsFromCacheAsync()).Where(x => deptIds.Contains(x.Id)).Select(d => new { d.Id, d.FullName });
                var roles = (await _cacheService.GetAllRolesFromCacheAsync()).Select(r => new { r.Id, r.Name });
                foreach (var user in userDtos)
                {
                    user.DeptName = depts.FirstOrDefault(x => x.Id == user.DeptId)?.FullName;

                    var roleIds = user.RoleIds.IsNullOrWhiteSpace()
                                            ? new List<long>()
                                            : user.RoleIds.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(x => long.Parse(x))
                                            ;
                    user.RoleNames = roles.Where(x => roleIds.Contains(x.Id)).Select(x => x.Name).ToString(",");
                }
            }

            var xdata = await _cacheService.GetOrganizationsSimpleTreeListAsync();
            return new PageModelVo<UserDto>(search, userDtos, total, xdata);
        }

        public async Task<UserInfoDto> GetUserInfoAsync(long id)
        {
            var userProfile = await _userRepository.QueryByClauseAsync(u => new UserProfileDto
            {
                Account = u.Account,
                Avatar = u.Avatar,
                Birthday = u.Birthday,
                DeptId = u.DeptId,
                //DeptFullName = u.Dept.FullName,
                Email = u.Email,
                Name = u.Name,
                Phone = u.Phone,
                RoleIds = u.RoleIds,
                Sex = u.Sex,
                Status = u.Status
            }, x => x.Id == id);

            if (userProfile == null)
            {
                return null;
            }

            var userInfoDto = new UserInfoDto { Id = id, Profile = userProfile };

            if (userProfile.RoleIds.IsNotNullOrEmpty())
            {
                var roleIds = userProfile.RoleIds.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(x => long.Parse(x));
                var roles = _roleRepository
                                                .QueryListByClause(x => roleIds.Contains(x.Id))
                                                .Select(r => new { r.Id, r.Tips, r.Name })
                                                .ToList();
                foreach (var role in roles)
                {
                    userInfoDto.Roles.Add(role.Tips);
                    userInfoDto.Profile.Roles.Add(role.Name);
                }

                //var roleMenus = await _menuRepository.GetMenusByRoleIdsAsync(roleIds.ToArray(), true);
                //if (roleMenus.IsNotNullOrEmpty())
                //    userInfoDto.Permissions.AddRange(roleMenus.Select(x => x.Url).Distinct());
            }

            return userInfoDto;
        }
    }
}
